import { Program } from "../render/Program";
import { ViewType } from "../util/StructArray";
// const viewTypes = {
//   "Int8": Int8Array,
//   "Uint8": Uint8Array,
//   "Int16": Int16Array,
//   "Uint16": Uint16Array,
//   "Int32": Int32Array,
//   "Uint32": Uint32Array,
//   "Float32": Float32Array
// };

interface LayoutAttribute {
  name: string;
  type: ViewType;
  components?: number;
}

interface Binder {
  property: string;

  // populatePaintArray(layer: StyleLayer,
  //                    paintArray: StructArray,
  //                    statistics: PaintPropertyStatistics,
  //                    start: number,
  //                    length: number,
  //                    feature: Feature): void;

  defines(): Array<string>;

  setUniforms(gl: WebGLRenderingContext,
              program: Program,
              value: any): void;
              // layer: StyleLayer,
              // globalProperties: { zoom: number }): void;
}

// 绑定uniform常量
export class ConstantBinder implements Binder {
  name: string;
  type: string;
  property: string;
  useIntegerZoom: boolean;

  // new ConstantBinder('color', 'color', 'fill-color', false)
  constructor(name: string, type: string, property: string, useIntegerZoom: boolean) {
      this.name = name;
      this.type = type;
      this.property = property;
      this.useIntegerZoom = useIntegerZoom;
  }

  defines() {
      return [`#define HAS_UNIFORM_u_${this.name}`];
  }

  populatePaintArray() {}

  setUniforms(gl: WebGLRenderingContext, program: Program, value: any) {
      if (this.type === "color") {
          gl.uniform4fv(<WebGLUniformLocation>program.uniforms[`u_${this.name}`], value);
      } else {
          gl.uniform1f(program.uniforms[`u_${this.name}`], value);
      }
  }
}

export interface ProgramInterface {
  layoutAttributes: Array<LayoutAttribute>;
  // indexArrayType: Class<StructArray>,
  // dynamicLayoutAttributes?: Array<LayoutAttribute>,
  // paintAttributes?: Array<PaintAttribute>,
  // indexArrayType2?: Class<StructArray>
}
/**
* ProgramConfiguration contains the logic for binding style layer properties and tile
* layer feature data into GL program uniforms and vertex attributes.
*
* Non-data-driven property values are bound to shader uniforms. Data-driven property
* values are bound to vertex attributes.
  In order to support a uniform GLSL syntax over
* both, the GL team defines a `#pragma`
* abstraction, which ProgramConfiguration is responsible for implementing. At runtime,
* it examines the attributes of a particular layer, combines this with fixed knowledge
* about how layers of the particular type are implemented, and determines which uniforms
* and vertex attributes will be required. It can then substitute the appropriate text
* into the shader source code, create and link a program, and bind the uniforms and
* vertex attributes in preparation for drawing.
*
* When a vector tile is parsed, this same configuration information is used to
* populate the attribute buffers needed for data-driven styling using the zoom
* level and feature property data.
*/
export class ProgramConfiguration {
  binders: { [key: string]: Binder };
  cacheKey: string;
  interface?: ProgramInterface;

  constructor() {
    this.binders = {};
    this.cacheKey = "";
  }

  static createBasicFill() {
    const self = new ProgramConfiguration();

    self.binders.color = new ConstantBinder("color", "color", "fill-color", false);
    self.cacheKey += `/u_color`;

    self.binders.opacity = new ConstantBinder("opacity", "number", "fill-opacity", false);
    self.cacheKey += `/u_opacity`;

    return self;
  }

  defines(): Array<string> {
    const result: Array<string> = [];
    for (const name in this.binders) {
        result.push.apply(result, this.binders[name].defines());
    }
    return result;
  }
}